/*
 * (C) APUE
 * Get enviroment string
 */
#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>

#define	NOSPC	-1
#define	NOENT	-2
#define	ARG_MAX	1024

static char envbuf[ARG_MAX];
extern char **environ;
pthread_mutex_t env_mutex;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;	/* every thread call thread_init just once */

/*
 * Set thread attributions and initialize mutex
 */
static void thread_init()
{
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);	/* set mutex attribute type */
	pthread_mutex_init(&env_mutex, &attr);	/* initialize mutex */
	pthread_mutexattr_destory(&attr);
}

char *getenv(const char *name)
{
	int i, len;
	len = strlen(name);
	for (i = 0; environ[i] != NULL; i++)
		if (((strncmp(name, environ[i], len)) == 0) && (environ[i][len] == '='))
		{
			strcpy(envbuf, &environ[i][len + 1]);
			return (envbuf);
		}

	return (NULL);
}

/*
 * A reentrant and thread-safe version
 * For reentrant, caller must provide seperation buffer of themselves.
 * For thread-safe, getenv_r should lock the environ[] so that other threads cannot modify it when using.
 */
int getenv_r(const char *name, char *buf, int buflen)
{
	int i, len, olen;
	pthread_once(&init_done, thread_init);	/* every thread call thread_init just once */
	len = strlen(name);

	pthread_mutex_lock(&env_mutex);	/* lock environ vector */
	for (i = 0; environ[i] != NULL; i++)
		if (((strncmp(name, environ[i], len)) == 0) && (environ[i][len] == '='))
		{
			olen = strlen(&environ[i][len + 1]); /* calculate value length */
			if (olen < buflen) 	/* buffer is non enough space */
			{
				pthread_mutex_unlock(&env_mutex);
				return (NOSPC);
			}
			strcpy(buf, &environ[i][len + 1]);
			pthread_mutex_unlock(&env_mutex);
			return (0);
		}
	pthread_mutex_unlock(&env_mutex);	/* unlock environ vector */
	return (NOENT);	/* not found */
}


